﻿/*
 * Copyright 2014 Francois Karman
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using Quartz.WebManager.Logic;
using Quartz.WebManager.Model;
using Quartz.WebManager.Web.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace Quartz.WebManager.Web.Controllers
{
    /// <summary>
    /// Encapsulates the logic that add a new job.
    /// </summary>
    public class AddJobController : Controller
    {
        /// <summary>
        /// The starting page of the add job process.
        /// </summary>
        /// <returns>A redirection to the first step.</returns>
        public ActionResult Index()
        {
            return this.RedirectToAction("Step1");
        }

        /// <summary>
        /// Starts the job creation process.
        /// </summary>
        /// <returns>The view associated with this page.</returns>
        public ActionResult Step1()
        {
            IEnumerable<JobDefinition> model = JobDefinitionManager.Cache;
            return this.View(model);
        }

        /// <summary>
        /// Provides the detailed information about the task.
        /// </summary>
        /// <param name="name">The display name of the job.</param>
        /// <returns>The view associated with this page.</returns>
        [HttpGet]
        public ActionResult Step2(string name)
        {
            JobDefinition definition = JobDefinitionManager.Cache.FirstOrDefault(j => j.DisplayName == name);
            if (definition == null)
            {
                throw new ArgumentException("name", "The job is unknown");
            }

            JobDetail model = new JobDetail() { Definition = definition, JobType = definition.TypeName };
            return this.View(model);
        }

        /// <summary>
        /// Creates a job.
        /// </summary>
        /// <param name="model">The detailed information of the job.</param>
        /// <returns>The view associated with the page.</returns>
        [HttpPost]
        public ActionResult Step2(JobDetail model)
        {
            if (model == null)
            {
                throw new ArgumentNullException("model");
            }

            JobDefinition definition = JobDefinitionManager.Cache.FirstOrDefault(j => j.TypeName == model.JobType);
            if (definition == null)
            {
                throw new ArgumentException("name", "The job type is unknown");
            }
            else if (definition.TypeName != model.JobType)
            {
                throw new ArgumentException("model", "The job type is invalid");
            }

            // Apply the definition to the model and validates all the properties
            model.Definition = definition;
            foreach (var error in PropertyValidator.Instance.Validate(model))
            {
                string key = string.Format("Properties[{0}]", error.Key);
                ModelState.AddModelError(key, error.Value);
            }

            if (ModelState.IsValid)
            {
                try
                {
                    JobKey key = JobDetailManager.SaveOne(model, false);
                    return this.RedirectToAction("Step3", new { jobName = key.Name, jobGroup = key.Group });
                }
                catch (JobPersistenceException e)
                {
                    if (e.InnerException != null && e.InnerException is ObjectAlreadyExistsException)
                    {
                        ModelState.AddModelError("", "A job with the same name and group already exist");
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            return this.View(model);
        }

        /// <summary>
        /// Provides the trigger for the task.
        /// </summary>
        /// <param name="jobName">The name of the task.</param>
        /// <param name="jobGroup">The group of the task.</param>
        /// <returns>The view associated with the page.</returns>
        [HttpGet]
        public ActionResult Step3(string jobName, string jobGroup)
        {
            JobKey key = JobKey.Create(jobName, jobGroup);
            JobDetail detail = JobDetailManager.RetrieveOne(key, true);

            if (detail.Triggers.Count == 0)
            {
                // Normal path - the job has no trigger and a form is displayed to create the first one
                WebTrigger model = new WebTrigger();
                model.JobDetail = detail;
                model.JobGroup = jobGroup;
                model.JobName = jobName;

                return this.View(model);
            }
            else if (detail.Triggers.Count == 1)
            {
                // Final path - one trigger is created, a simple 'success' page is displayed
                return this.View("Final", detail);
            }
            else
            {
                // More that one trigger - definitively not the normal path
                throw new ArgumentException("The job is already created and initialized", "jobName");
            }
        }

        /// <summary>
        /// Stores the data about the trigger.
        /// </summary>
        /// <param name="model">The details about the trigger.</param>
        /// <returns>The view associated with this page.</returns>
        [HttpPost]
        public ActionResult Step3(WebTrigger model)
        {
            if (this.ModelState.IsValid)
            {
                // Try to store the data
                try
                {
                    TriggerManager.SaveOne(model);
                }
                catch (ObjectAlreadyExistsException e)
                {
                    this.ModelState.AddModelError("", "A trigger with the same name and group already exist");
                }
            }

            if (this.ModelState.IsValid)
            {
                // The view for step 3 will display a 'success' page if the job has a trigger
                return this.RedirectToAction("Step3", new { jobName = model.JobName, jobGroup = model.JobGroup });
            }
            else
            {
                return this.View(model);
            }
        }
    }
}
